//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<XEngineView.h>
///	@path	~/src/editor/wm/
///	@date	2008/10/24
///	@brief	.

#pragma once

#pragma message("Compiling XEngineView.h ...")

#include "editor/editor.h"

#include "gui/input/input.h"

#include "engine/xcore/CInputEvent.h"
#include "engine/xcore/CWindowEvent.h"
#include "engine/xcore/xeres_engine.h"

namespace xeres {

	/*! 
		\class	XEngineView
		\brief
	*/
	class XEngineView : public CWindowImpl< XEngineView > , public InputDispatcher
	{
	public:
		DECLARE_WND_CLASS( _T("xeres__XEngineView") )

		/// \ctor
		XEngineView( void ) {
		}
		/// \dtor
		~XEngineView( void ) {
		}

		BOOL PreTranslateMessage(MSG* pMsg)
		{
			UNREFERENCED_PARAMETER( pMsg );
			return FALSE;
		}

		BEGIN_MSG_MAP(XEngineView)
			MESSAGE_HANDLER(WM_ACTIVATE , OnActivate)
			MESSAGE_HANDLER(WM_CREATE, OnCreate)
			MESSAGE_HANDLER(WM_PAINT , OnPaint)
			MESSAGE_HANDLER(WM_SIZE , OnSize)
			MESSAGE_HANDLER(WM_ENTERSIZEMOVE , OnEnterSizeMove)
			MESSAGE_HANDLER(WM_EXITSIZEMOVE , OnExitSizeMove)
			MESSAGE_HANDLER(WM_ERASEBKGND , OnEraseBkgnd)
		// Dispatch message to engine.
			if( DispatchMessage( m_hWnd , uMsg , wParam , lParam ) == INPUT_HANDLED ) return TRUE; else bHandled = FALSE;
		END_MSG_MAP()

		inline WindowEventInfo MakeEvent( void )
		{
			WindowEventInfo info;
			RECT rect;
			GetClientRect( &rect );
			info.m_rect.set( rect.left , rect.top , rect.right , rect.bottom );
			return info;
		}

		LRESULT OnActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/) {
			// Dummy filter to refresh dispatcher states.
			if( m_activate.IsValid() )
			{
				WindowEventInfo info = MakeEvent();
				info.m_tag = (LOWORD(wParam) == WA_INACTIVE) ? WINDOW_EVENT_INACTIVATE : WINDOW_EVENT_ACTIVATE;
				m_activate->Active( &info );
			}
			return S_OK;
		}
		LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) {

			// Setup ticking timer (30fps).
			this->SetTimer( TIMER_TICK , static_cast<int>( 1000.f / 30.f ) );

			return S_OK;
		}
		LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) {
			CPaintDC dc( m_hWnd );
			if( m_paint.IsValid() )
			{
				WindowEventInfo info = MakeEvent();
				info.m_tag = WINDOW_EVENT_NONE;
				m_paint->Active( &info );
			}
			return S_OK;
		}
		LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled ) {
			if( m_size.IsValid() )
			{
				WindowEventInfo info = MakeEvent();
				info.m_tag = ( wParam == SIZE_MINIMIZED ) ? WINDOW_EVENT_MINIMIZED : WINDOW_EVENT_NONE;;
				m_size->Active( &info );
			}
			return S_OK;
		}
		LRESULT OnEnterSizeMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled ) {
			if( m_size.IsValid() )
			{
				WindowEventInfo info = MakeEvent();
				info.m_tag = WINDOW_EVENT_ENTERSIZING;
				m_size->Active( &info );
			}
			return S_OK;
		}
		LRESULT OnExitSizeMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled ) {
			if( m_size.IsValid() )
			{
				WindowEventInfo info = MakeEvent();
				info.m_tag = WINDOW_EVENT_EXITSIZING;
				m_size->Active( &info );
			}
			return S_OK;
		}
		LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) {
			return S_OK;
		}

		/// \brief	Attach engine.
		bool AttachEngine( Engine& engine )
		{
			if( m_activate.IsValid() || m_size.IsValid() || m_paint.IsValid() || m_keyEvent.IsValid() || m_mouseEvent.IsValid() )
				return false;

			m_activate = new CWindowEvent;
			m_size = new CWindowEvent;
			m_paint = new CWindowEvent;
			m_keyEvent = new CInputEvent;
			m_mouseEvent = new CInputEvent;

			engine.RegisterEvent( _S("message/activate") , m_activate , true );
			engine.RegisterEvent( _S("message/size") , m_size , true );
			engine.RegisterEvent( _S("message/paint") , m_paint , true );
			engine.RegisterEvent( _S("input/keyboard") , m_keyEvent , false );
			engine.RegisterEvent( _S("input/mouse") , m_mouseEvent , false );
			
			if( !engine.InsertStage( _S("input/input") , _S("") , bind( &XEngineView::DispatchInput , this ) ) )
			{
				return false;
			}
			return true;
		}

		void DispatchInput( void )
		{
			// for all inputs
			if( !m_messageQueue.empty() )
			{
				for( size_t i = 0 , total = m_messageQueue.size() ; i < total ; ++i )
				{
					Input * pInput = (Input*)( m_messageQueue[i].buffer );
					if( pInput->GetType() == Input::TYPE_KEY )
					{
						m_keyEvent->Active( pInput );
					}
					else if( pInput->GetType() == Input::TYPE_MOUSE )
					{
						m_mouseEvent->Active( pInput );
					}
					else
					{
						assert(false);
					}
				}
				m_messageQueue.clear();
			}
		}

		///	\brief	Process key input message.
		virtual HandleInput OnKeyInput( const KeyInput& input )
		{
			_InputMessage iBuffer;
			memset( &iBuffer , 0 , sizeof(_InputMessage) );
			memcpy( &iBuffer , &input , sizeof(KeyInput) );
			m_messageQueue.push_back( iBuffer );
			return INPUT_HANDLED;
		}

		///	\brief	Process mouse input message.
		virtual HandleInput OnMouseInput( const MouseInput& input )
		{
			_InputMessage iBuffer;
			memset( &iBuffer , 0 , sizeof(_InputMessage) );
			memcpy( &iBuffer , &input , sizeof(MouseInput) );
			m_messageQueue.push_back( iBuffer );
			return INPUT_HANDLED;
		}

	private:

		//@ data

		// activate app
		Ref< CWindowEvent >		m_activate;
		// on size
		Ref< CWindowEvent >		m_size;
		// on paint
		Ref< CWindowEvent >		m_paint;
		// key event
		Ref< CInputEvent >		m_keyEvent;
		// mouse event
		Ref< CInputEvent >		m_mouseEvent;

		// input message queue
		struct _InputMessage
		{
#define	MAXSIZE( A , B )		sizeof(A) > sizeof(B) ? sizeof(A) : sizeof(B)
			uchar buffer[ MAXSIZE( KeyInput , MouseInput ) ];
#undef MAXSIZE
		};

		// queue
		std::deque<_InputMessage>	m_messageQueue;
	};

} // namespace xeres
